package com.sl.transport.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.itheima.em.sdk.EagleMapTemplate;
import com.itheima.em.sdk.enums.ProviderEnum;
import com.itheima.em.sdk.vo.Coordinate;
import com.sl.transport.common.exception.SLException;
import com.sl.transport.common.util.ObjectUtil;
import com.sl.transport.common.util.PageResponse;
import com.sl.transport.domain.DispatchConfigurationDTO;
import com.sl.transport.domain.OrganDTO;
import com.sl.transport.domain.TransportLineNodeDTO;
import com.sl.transport.domain.TransportLineSearchDTO;
import com.sl.transport.entity.line.TransportLine;
import com.sl.transport.entity.node.AgencyEntity;
import com.sl.transport.entity.node.BaseEntity;
import com.sl.transport.entity.node.OLTEntity;
import com.sl.transport.entity.node.TLTEntity;
import com.sl.transport.enums.DispatchMethodEnum;
import com.sl.transport.enums.ExceptionEnum;
import com.sl.transport.enums.TransportLineEnum;
import com.sl.transport.repository.TransportLineRepository;
import com.sl.transport.service.CostConfigurationService;
import com.sl.transport.service.DispatchConfigurationService;
import com.sl.transport.service.OrganService;
import com.sl.transport.service.TransportLineService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class TransportLineServiceImpl implements TransportLineService {

    @Resource
    private TransportLineRepository transportLineRepository;

    @Resource
    private OrganService organService;

    @Resource
    private EagleMapTemplate eagleMapTemplate;

    @Resource
    private CostConfigurationService costConfigurationService;

    @Resource
    private DispatchConfigurationService dispatchConfigurationService;

    /**
     * 新增路线
     *
     * @param transportLine 路线数据
     * @return 是否成功
     */
    @Override
    public Boolean createLine(TransportLine transportLine) {
        // 1. 根据type获取对应枚举
        TransportLineEnum transportLineEnum = TransportLineEnum.codeOf(transportLine.getType());
        if (ObjectUtil.isEmpty(transportLineEnum)) {
            throw new SLException(ExceptionEnum.TRANSPORT_LINE_TYPE_ERROR);
        }
        // 2. 起点终点不能一样
        if (ObjectUtil.equal(transportLine.getStartOrganId(), transportLine.getEndOrganId())) {
            throw new SLException(ExceptionEnum.START_END_ORGAN_NOT_FOUND);
        }
        // 3. 定义起点和终点
        BaseEntity firstNode;
        BaseEntity secondNode;
        switch (transportLineEnum) {
            case TRUNK_LINE:
                //  干线 一级转运中心到一级转运中心
                firstNode = OLTEntity.builder().bid(transportLine.getStartOrganId()).build();
                secondNode = OLTEntity.builder().bid(transportLine.getEndOrganId()).build();
                break;
            case BRANCH_LINE:
                //  支线 二级转运中心（分拣中心）到一级转运中心（转运中心）
                firstNode = TLTEntity.builder().bid(transportLine.getStartOrganId()).build();
                secondNode = OLTEntity.builder().bid(transportLine.getEndOrganId()).build();
                break;
            case CONNECT_LINE:
                //  接驳路线 二级转运中心（分拣中心）到网点
                firstNode = AgencyEntity.builder().bid(transportLine.getStartOrganId()).build();
                secondNode = TLTEntity.builder().bid(transportLine.getEndOrganId()).build();
                break;
            default:
                throw new SLException(ExceptionEnum.TRANSPORT_LINE_TYPE_ERROR);
        }
        // 4. 判断节点到节点间的路线是否已存在
        Long count = transportLineRepository.queryCount(firstNode, secondNode);
        if (count > 0) {
            throw new SLException(ExceptionEnum.TRANSPORT_LINE_ALREADY_EXISTS);
        }
        // 5. 填充属性
        transportLine.setId(null);
        transportLine.setCreated(System.currentTimeMillis());
        transportLine.setUpdated(System.currentTimeMillis());
        // 6. 基于地图填充其他属性
        initForMap(transportLine);
        // 6. 保存到neo4j
        Long countTrans = transportLineRepository.create(firstNode, secondNode, transportLine);
        return countTrans > 0;
    }

    /**
     * 基于eagleMap为TransportLine填充属性
     */
    private void initForMap(TransportLine transportLine) {
        //  1. 根据起点ID查询网点 经纬度不能为空
        OrganDTO start = organService.findByBid(transportLine.getStartOrganId());
        if (ObjectUtil.hasEmpty(start, start.getLongitude(), start.getLatitude())) {
            throw new SLException("请完善机构信息");
        }
        //  2. 根据终点ID查询网点 经纬度不能为空
        OrganDTO end = organService.findByBid(transportLine.getEndOrganId());
        if (ObjectUtil.hasEmpty(end, end.getLongitude(), end.getLatitude())) {
            throw new SLException("请完善机构信息");
        }
        //  3. 查询起点到终点的驾驶路线
        Coordinate origin = new Coordinate(start.getLongitude(), start.getLatitude());
        Coordinate destination = new Coordinate(end.getLongitude(), end.getLatitude());
        Map<String, Object> params = new HashMap<>();
        params.put("show_fields", "cost");
        String driving = eagleMapTemplate.opsForDirection().driving(ProviderEnum.AMAP, origin, destination, params);
        //  4. 解析耗时时间
        JSONObject jsonObject = JSONUtil.parseObj(driving);
        Long time = Convert.toLong(JSONUtil.getByPath(jsonObject, "route.paths[0].cost.duration"));
        transportLine.setTime(time);
        //  5. 解析路线距离
        Double distance = Convert.toDouble(JSONUtil.getByPath(jsonObject, "route.paths[0].distance"));
        transportLine.setDistance(distance);
        //  6. 计算路线成本 公式：cost = 距离 / 1000 * cost
        Double costByType = costConfigurationService.findCostByType(transportLine.getType());
        transportLine.setCost(distance / 1000 * costByType);
    }

    /**
     * 更新路线
     *
     * @param transportLine 路线数据
     * @return 是否成功
     */
    @Override
    public Boolean updateLine(TransportLine transportLine) {
        //1.查询数据是否存在
        TransportLine transportLineData = this.queryById(transportLine.getId());
        if (ObjectUtil.isEmpty(transportLineData)) {
            throw new SLException(ExceptionEnum.TRANSPORT_LINE_NOT_FOUND);
        }
        //2.拷贝数据，忽略空字段，忽略不可修改字段
        BeanUtil.copyProperties(transportLine, transportLineData, CopyOptions.create().setIgnoreNullValue(true)
                .setIgnoreProperties("type", "startOrganId", "startOrganName", "endOrganId", "endOrganName"));
        //3.设置更新时间
        transportLineData.setUpdated(System.currentTimeMillis());
        Long count = this.transportLineRepository.update(transportLineData);
        return count > 0;
    }

    /**
     * 删除路线
     *
     * @param id 路线id
     * @return 是否成功
     */
    @Override
    public Boolean deleteLine(Long id) {
        Long count = this.transportLineRepository.remove(id);
        return count > 0;
    }

    /**
     * 分页查询路线
     *
     * @param transportLineSearchDTO 搜索参数
     * @return 路线列表
     */
    @Override
    public PageResponse<TransportLine> queryPageList(TransportLineSearchDTO transportLineSearchDTO) {
        return this.transportLineRepository.queryPageList(transportLineSearchDTO);
    }

    /**
     * 查询两个网点之间最短的路线，最大查询深度为：10
     *
     * @param startId 开始网点id
     * @param endId   结束网点id
     * @return 路线
     */
    @Override
    public TransportLineNodeDTO queryShortestPath(Long startId, Long endId) {
        AgencyEntity start = AgencyEntity.builder().bid(startId).build();
        AgencyEntity end = AgencyEntity.builder().bid(endId).build();
        if (ObjectUtil.hasEmpty(start, end)) {
            throw new SLException(ExceptionEnum.START_END_ORGAN_NOT_FOUND);
        }
        return this.transportLineRepository.findShortestPath(start, end);
    }

    /**
     * 查询两个网点之间成本最低的路线，最大查询深度为：10
     *
     * @param startId 开始网点id
     * @param endId   结束网点id
     * @return 路线集合
     */
    @Override
    public TransportLineNodeDTO findLowestPath(Long startId, Long endId) {
        AgencyEntity start = AgencyEntity.builder().bid(startId).build();
        AgencyEntity end = AgencyEntity.builder().bid(endId).build();
        if (ObjectUtil.hasEmpty(start, end)) {
            throw new SLException(ExceptionEnum.START_END_ORGAN_NOT_FOUND);
        }
        //查询深度为10，查1条
        List<TransportLineNodeDTO> pathList = this.transportLineRepository.findPathList(start, end, 10, 1);
        if (CollUtil.isNotEmpty(pathList)) {
            //取第一条数据
            return CollUtil.getFirst(pathList);
        }
        return null;
    }

    /**
     * 根据调度策略查询路线
     *
     * @param startId 开始网点id
     * @param endId   结束网点id
     * @return 路线
     */
    @Override
    public TransportLineNodeDTO queryPathByDispatchMethod(Long startId, Long endId) {
        //调度配置方式
        DispatchConfigurationDTO configuration = this.dispatchConfigurationService.findConfiguration();
        Integer method = configuration.getDispatchMethod();
        //判断调度方式
        if (ObjectUtil.equal(DispatchMethodEnum.LOWEST_PATH.getCode(), method)) {
            return this.findLowestPath(startId, endId);
        } else {
            return this.queryShortestPath(startId, endId);
        }
    }

    /**
     * 根据ids批量查询路线
     *
     * @param ids id列表
     * @return 路线列表
     */
    @Override
    public List<TransportLine> queryByIds(Long... ids) {
        return this.transportLineRepository.queryByIds(ids);
    }

    /**
     * 根据id查询路线
     *
     * @param id 路线id
     * @return 路线数据
     */
    @Override
    public TransportLine queryById(Long id) {
        return this.transportLineRepository.queryById(id);
    }

}
